File: src\RoslynAnalyzers\Utilities\Compiler\PooledObjects\TemporarySet`1.cs
Web Access
Project: src\src\RoslynAnalyzers\Text.Analyzers\Core\Text.Analyzers.csproj (Text.Analyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
 
namespace Analyzer.Utilities.PooledObjects
{
    [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Not used in this context")]
    internal struct TemporarySet<T>
    {
#pragma warning disable CS0649 // Field 'TemporarySet<T>.Empty' is never assigned to, and will always have its default value
        public static readonly TemporarySet<T> Empty;
#pragma warning restore CS0649 // Field 'TemporarySet<T>.Empty' is never assigned to, and will always have its default value
 
        /// <summary>
        /// An empty set used for creating non-null enumerators when no items have been added to the set.
        /// </summary>
        private static readonly HashSet<T> EmptyHashSet = new();
 
        // 🐇 PERF: use PooledHashSet<T> instead of PooledConcurrentSet<T> due to allocation overhead in
        // clearing the set for returning it to the pool.
        private PooledHashSet<T>? _storage;
 
        public readonly Enumerable NonConcurrentEnumerable
            => new(_storage ?? EmptyHashSet);
 
        public void Free(CancellationToken cancellationToken)
        {
            Interlocked.Exchange(ref _storage, null)?.Free(cancellationToken);
        }
 
        private PooledHashSet<T> GetOrCreateStorage(CancellationToken cancellationToken)
        {
            if (_storage is not { } storage)
            {
                var newStorage = PooledHashSet<T>.GetInstance();
                storage = Interlocked.CompareExchange(ref _storage, newStorage, null) ?? newStorage;
                if (storage != newStorage)
                {
                    // Another thread initialized the value. Make sure to release the unused object.
                    newStorage.Free(cancellationToken);
                }
            }
 
            return storage;
        }
 
        public bool Add(T item, CancellationToken cancellationToken)
        {
            var storage = GetOrCreateStorage(cancellationToken);
            lock (storage)
            {
                return storage.Add(item);
            }
        }
 
        public readonly bool Contains(T item)
        {
            if (_storage is not { } storage)
                return false;
 
            lock (storage)
            {
                return storage.Contains(item);
            }
        }
 
        public readonly bool Contains_NonConcurrent(T item)
        {
            if (_storage is not { } storage)
                return false;
 
            return storage.Contains(item);
        }
 
        public readonly bool Any_NonConcurrent()
        {
            if (_storage is not { } storage)
                return false;
 
            return storage.Count > 0;
        }
 
        public readonly Enumerator GetEnumerator_NonConcurrent()
        {
            return new Enumerator((_storage ?? EmptyHashSet).GetEnumerator());
        }
 
        public readonly IEnumerable<T> AsEnumerable_NonConcurrent()
        {
            return (_storage ?? EmptyHashSet).AsEnumerable();
        }
 
        public readonly struct Enumerable
        {
            private readonly HashSet<T> _set;
 
            public Enumerable(HashSet<T> set)
            {
                _set = set;
            }
 
            public Enumerator GetEnumerator()
                => new(_set.GetEnumerator());
        }
 
        public struct Enumerator
        {
            private HashSet<T>.Enumerator _enumerator;
 
            public Enumerator(HashSet<T>.Enumerator enumerator)
            {
                _enumerator = enumerator;
            }
 
            public bool MoveNext()
                => _enumerator.MoveNext();
 
            public T Current
                => _enumerator.Current;
        }
    }
}